package com.hlg.xboot.service;

import com.hlg.xboot.core.export.ExportExcel;
import com.hlg.xboot.dao.BaseRepository;
import com.hlg.xboot.exceptions.ParameterException;
import com.hlg.xboot.exceptions.RecordNotFoundException;
import com.hlg.xboot.exceptions.ServerBizException;
import com.hlg.xboot.model.AbstractModel;
import com.hlg.xboot.model.BaseModel;
import com.hlg.xboot.model.sys.SysUser;
import com.hlg.xboot.utils.ExcelHelper;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.multipart.MultipartFile;

import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Created by yangxin on 16/4/4.
 */
public abstract class BaseService<E extends AbstractModel, R extends BaseRepository>
        extends SpecificationService<E> implements ExportExcel<E> {

    protected Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    protected R repository;

    public List<E> add(List<E> items) throws ServerBizException {
        return repository.save(items);
    }

    public E add(E e) throws ServerBizException {
        return (E) repository.save(e);
    }

    public E add(E e, MultipartFile[] files) throws ServerBizException {
        return (E) repository.save(e);
    }

    public void delete(E e) throws ServerBizException {
        repository.delete(e);
    }

    public E delete(Serializable id) throws ServerBizException {
        E e = findById(id);
        if (e == null) {
            throw new RecordNotFoundException();
        }
        delete(e);
        return e;
    }

    public E deleteById(Serializable id) throws ServerBizException {
        E e = findById(id);
        if (e == null) {
            throw new RecordNotFoundException();
        }
        delete(e);
        return e;
    }

    public void delete(List<E> items) throws ServerBizException {
        repository.deleteInBatch(items);
    }

    public void deleteInBatch(String ids) throws ServerBizException {
        if (StringUtils.isEmpty(ids)) {
            throw new ParameterException("参数不能为空");
        }
        List<Serializable> list = Stream.of(ids.split(",")).map((id) -> {
            try {
                return Long.parseLong(id);
            } catch (NumberFormatException e) {
                return id;
            }
        }).collect(Collectors.toList());
        delete(repository.findAll(list));
    }

    public E update(E e) throws ServerBizException {
        if (e instanceof BaseModel) {
            ((BaseModel) e).setUpdateTime(new Date());
        }
        return (E) repository.save(e);
    }

    public E update(E e, MultipartFile[] files) throws ServerBizException {
        return (E) repository.save(e);
    }

    public E findById(Serializable id) throws ServerBizException {
        E e;
        try {
            Long realId = Long.parseLong(id.toString());
            e = (E) repository.findOne(realId);
        } catch (NumberFormatException ex) {
            e = (E) repository.findOne(id.toString());
        }

        if (e == null) {
            throw new RecordNotFoundException();
        }
        return e;
    }

    /**
     * 查询总条数
     *
     * @return
     * @throws ServerBizException
     */
    public Long count() throws ServerBizException {
        return repository.count();
    }

    /**
     * 根据条件查询总数
     *
     * @param spec
     * @return
     * @throws ServerBizException
     */
    public Long count(Specification<E> spec) throws ServerBizException {
        return repository.count(spec);
    }

    /**
     * 根据模型查询总数
     *
     * @param e 模型
     * @return
     * @throws ServerBizException
     */
    public Long count(E e) throws ServerBizException {
        return repository.count(buildSpecification(e));
    }

    /**
     * 根据条件分页查询所有数据
     *
     * @param specification 查询条件
     * @param pageable      分页参数
     * @return
     * @throws ServerBizException
     */
    public Page<E> findAll(Specification<E> specification, Pageable pageable) throws ServerBizException {
        return repository.findAll(specification, pageable);
    }

    /**
     * 根据查询条件分页查询，并默认按照创建时间倒序排序
     *
     * @param specification 查询条件
     * @param page          当前页码
     * @param size          每页显示的记录数
     * @return
     * @throws ServerBizException
     */
    public Page<E> findAll(Specification<E> specification, int page, int size) throws ServerBizException {
        PageRequest pageable = new PageRequest(page, size);
        return repository.findAll(specification, pageable);
    }

    /**
     * 根据查询条件分页查询和排序
     *
     * @param specification 查询条件
     * @param page          当前页码
     * @param size          每页显示的记录数
     * @param sortDirt      排序方向，正序：ASC，倒序：DESC
     * @param sortName      排序字段
     * @return
     * @throws ServerBizException
     * @see Sort.Direction
     */
    public Page<E> findAll(Specification<E> specification, int page, int size, String sortDirt, String sortName) throws ServerBizException {
        Sort sort = null;
        if (StringUtils.isNotEmpty(sortDirt) && StringUtils.isNotEmpty(sortName)) {
            sort = new Sort(Sort.Direction.fromString(sortDirt), sortName);
        }
        PageRequest pageable = new PageRequest(page, size, sort);
        return repository.findAll(specification, pageable);
    }

    /**
     * 查询所有数据
     *
     * @param specification 查询条件
     * @param sortDirt      排序方向，正序：ASC，倒序：DESC
     * @param sortName      排序字段
     * @return
     * @throws ServerBizException
     * @see Sort.Direction
     */
    public List<E> findAll(Specification<E> specification, String sortDirt, String sortName) throws ServerBizException {
        Sort sort;
        if (StringUtils.isNotEmpty(sortDirt) && StringUtils.isNotEmpty(sortName)) {
            sort = new Sort(Sort.Direction.fromString(sortDirt), sortName);
        } else {
            sort = new Sort(Sort.Direction.DESC, "createTime");
        }
        return findAll(specification, sort);
    }

    /**
     * 查询所有数据
     *
     * @param specification 查询条件
     * @param sort      排序方向，正序：ASC，倒序：DESC
     * @return
     * @throws ServerBizException
     * @see Sort.Direction
     */
    public List<E> findAll(Specification<E> specification, Sort sort) throws ServerBizException {
        return repository.findAll(specification, sort);
    }

    /**
     * 分页查询所有数据
     *
     * @param pageable 分页参数
     * @return
     */
    public Page<E> findAllByPage(Pageable pageable) throws ServerBizException {
        return repository.findAll(pageable);
    }

    /**
     * 根据条件查询
     *
     * @param specification
     * @return
     * @throws ServerBizException
     */
    public List<E> findAll(Specification<E> specification) throws ServerBizException {
        return repository.findAll(specification);
    }

    /**
     * 查询所有记录
     *
     * @return
     * @throws ServerBizException
     */
    public List<E> findAll() throws ServerBizException {
        return findAll(null);
    }

    /**
     * 根据查询条件分页查询
     *
     * @param e
     * @param page
     * @return
     * @throws Exception
     */
    public Page<E> find(E e, Pageable page) throws Exception {
        return findAll(buildSpecification(e), page);
    }

    public List<E> findAll(E e, Sort sort) throws Exception {
        return findAll(buildSpecification(e), sort);
    }

    @Override
    public List<String> getExportHeader() {
        return null;
    }

    @Override
    public List<List<String>> getExportRows(E e, Pageable pageable) throws Exception {
        return null;
    }

    @Override
    public String getExportFileName() {
        return "data";
    }

    public Integer batchImport(E e, ExcelHelper excelHelper, SysUser loginUser) throws ServerBizException {
        return 0;
    }

}
